#!/usr/bin/env python

import os
import sys
import getopt
import calendar
import xml.sax
import iso8601
import tempfile
import shutil
import yaml
import subprocess
from datetime import datetime
from xml.sax.handler import ContentHandler
from pytz import timezone

HOME_PATH=os.path.expanduser("~/.gps2exif")
DB_PATH=os.path.join(HOME_PATH,"db")

SARG_IMPORT='i'
LARG_IMPORT='import'
SARG_IMPORT_PREFIXED="-"+SARG_IMPORT
LARG_IMPORT_PREFIXED="--"+LARG_IMPORT

SARG_HELP='h'
LARG_HELP='help'
SARG_HELP_PREFIXED="-"+SARG_HELP
LARG_HELP_PREFIXED="--"+LARG_HELP

META_VERSION='version'
META_IMPORT_FILE='import_file'

class GPSImportDriver(object):
	def set_data_file(self, data_file):
		self.data_file = data_file

	def create_db_dirs(self, tstamp_from, tstamp_to):
		dir_to=os.path.join(DB_PATH, str(tstamp_to))
		if not os.path.exists(dir_to):
			os.mkdir(dir_to)
		dir_from = os.path.join(dir_to, str(tstamp_from))
		if not os.path.exists(dir_from):
			os.mkdir(dir_from)
		return dir_from

	def write_metadata(self, data_dir):
		f = open(os.path.join(data_dir, "meta.yaml"), "w")
		try:
			f.write(yaml.dump({META_VERSION:1,\
				META_IMPORT_FILE:self.data_file}, default_flow_style=False))
		finally:
			f.close()

class GPSGPXImportDriver(GPSImportDriver):
	def run(self):
		class GPXHandler(ContentHandler):
			def __init__(self, file_handle):
				self.file_handle = os.fdopen(file_handle, "w")

			def __reset_data(self):
				self.lat = None
				self.lon = None
				self.ele = None
				self.time = None
				self.chars_write_to = None

			def startDocument(self):
				self.__reset_data()
				self.start_time = None
				self.stop_time = None

			def endDocument(self):
				self.stop_time = self.time
				self.__point_data_collected()
				self.file_handle.close()
				self.stop_time = iso8601.parse_date(self.stop_time)
				self.start_time = iso8601.parse_date(self.start_time)

			def startElement(self, name, attrs):
				if name == 'trkpt':
					if not self.start_time and self.time:
						self.start_time = self.time
					self.__point_data_collected()
					attrs_names = attrs.getNames()
					if attrs and 'lat' in attrs_names \
						and 'lon' in attrs_names:
							(self.lat, self.lon) = (attrs.getValue('lat'), attrs.getValue('lon'))
				elif name == 'ele' and self.lat:
					self.chars_write_to = 'ele'
				elif name == 'time' and self.lat:
					self.chars_write_to = 'time'
				else:
					self.chars_write_to = None

			def characters (self, ch):
				if self.chars_write_to:
					curr_chars = getattr(self, self.chars_write_to)
					if curr_chars == None:
						curr_chars = ""
					setattr(self, self.chars_write_to, curr_chars + ch.strip())

			def __point_data_collected(self):
				if self.lat:
					self.file_handle.write(str(datetime_to_tstamp(iso8601.parse_date(self.time)))+\
						","+self.lat+","+self.lon+","+self.ele+"\r\n")
					self.__reset_data()

		(handle, path) = tempfile.mkstemp(dir=DB_PATH)
		try:
			handler = GPXHandler(handle)
			xml.sax.parse(self.data_file, handler)
			dir_for_data = self.create_db_dirs(datetime_to_tstamp(handler.start_time),\
				datetime_to_tstamp(handler.stop_time))
			shutil.move(path,os.path.join(dir_for_data,'data'))
			self.write_metadata(dir_for_data)
		except Exception as e:
			print (e)
			try:
				os.remove(path)
			except Exception as e:
				print str(e)

def datetime_to_tstamp(dt):
	return calendar.timegm(dt.timetuple())

def driver(data_file):
	ext = os.path.splitext(data_file)[-1].upper()
	if ext and ext.startswith('.'):
		ext = ext[1:]
	driver_name = "GPS" + ext + "ImportDriver"
	driver = None
	if ext and driver_name in globals():
		driver = apply(globals()[driver_name],())
		driver.set_data_file(data_file)
	else:
		print "Can not find import driver for data format '" + ext + "'"
	return driver

def register(args):
	for arg in args:
		dr = driver(arg)
		if dr:
			dr.run()

class ExifDataProvider(object):
	
	def __init__(self, file_path, tz=None):
		self.file_path = file_path
		self.tz = tz

	def get_creation_tstamp(self):
		p = subprocess.Popen(['exiv2', '-PEkt', self.file_path], stdout=subprocess.PIPE)
		(out, err) = p.communicate()
		p.wait()
		vals = out.split()
		i = vals.index('Exif.Photo.DateTimeOriginal')
		date = vals[i+1] + ':' + vals[i+2]
		date = datetime.strptime(date, '%Y:%m:%d:%H:%M:%S')
		tstamp = datetime_to_tstamp(date)
		return tstamp

	def to_deg(self, amount):
		degs = int(amount)
		frac = amount - degs
		print frac
		secs = frac * 3600
		mins = int(secs / 60)
		secs = int(100*round(secs % 60,2))
		return (abs(degs), abs(mins), abs(secs))

	def write_coords(self, lat, lon, ele):
		data = {}
		data['Exif.GPSInfo.GPSLatitudeRef'] = ('Ascii', lat < 0 and 'S' or 'N')
		data['Exif.GPSInfo.GPSLongitudeRef']= ('Ascii', lon < 0 and 'W' or 'E')
		data['Exif.GPSInfo.GPSAltitudeRef'] = ('Byte', 0)
		print "writing coords: " + str (lat) + " " + str(lon) + " " +str(ele)
		t = self.to_deg(lat)
		print t
		data['Exif.GPSInfo.GPSLatitude'] = ('Rational', '%d/1 %d/1 %d/100' % self.to_deg(lat))
		data['Exif.GPSInfo.GPSLongitude'] = ('Rational', '%d/1 %d/1 %d/100' % self.to_deg(lon))
		data['Exif.GPSInfo.GPSAltitude'] = ('Rational', '%d/10' % int(10*round(ele,1)))
		commands = ['exiv2']
		for k in data:
			commands.append('-M')
			commands.append('set %s %s %s' % (k, data[k][0], data[k][1]))
		commands.append(self.file_path)
		print commands
		p = subprocess.Popen(commands)
		p.wait()

def find_coords(path, tstamp):
	f = open(path)
	try:
		lines = f.readlines()
		for line in lines:
			splits = line.split(',')
			time = int(splits[0])
			if time >= tstamp:
				return (float(splits[1]), float(splits[2]), float(splits[3]))
		return None
	finally:
		f.close()

def process(argv, tz):
	for arg in argv:
		exif = ExifDataProvider(arg, tz)
		timestamp = exif.get_creation_tstamp()
		tills = os.listdir(DB_PATH)
		print tills
		tills = filter(lambda x: timestamp <= int(x), tills)
		print tills
		for till in tills:
			froms = os.listdir(os.path.join(DB_PATH, till))
			print froms
			froms = filter(lambda x: timestamp >= int(x), froms)
			print froms
			for fro in froms:
				path = os.path.join(DB_PATH, till, fro, 'data')
				coords = find_coords(path, timestamp)
				if coords:
					(lat, lon, ele) = coords
					exif.write_coords(lat, lon, ele)

def create_files_needed():
	try:
		os.mkdir(HOME_PATH)
		os.mkdir(DB_PATH)
	except:
		pass

def usage():
	prog_name = sys.argv[0]
	print "Usage: " + prog_name + " [options] file(s)\nOptions:"
	print SARG_HELP_PREFIXED + ", " + LARG_HELP_PREFIXED + "\n\t- prints this message"
	print SARG_IMPORT_PREFIXED + ", " + LARG_IMPORT_PREFIXED + " gps_data_file\n\t- imports gps data file"

def main(argv):
	create_files_needed()
	try:
		opts, args = getopt.getopt(argv,
			SARG_HELP+SARG_IMPORT,
			[LARG_HELP, LARG_IMPORT])
	except getopt.GetoptError, err:
		print str(err)
		usage()
		sys.exit(2)
	for o, a in opts:
		if o in (SARG_HELP_PREFIXED, LARG_HELP_PREFIXED):
			usage()
			sys.exit()
		elif o in (SARG_IMPORT_PREFIXED, LARG_IMPORT_PREFIXED):
			register(args)
			sys.exit()
	process(args, timezone('Europe/Vilnius'))

if __name__ == "__main__":
	main(sys.argv[1:])

